package com.xhj.mybatis.scripting.defaults;

import com.xhj.mybatis.executor.parameter.ParameterHandler;
import com.xhj.mybatis.mapping.BoundSql;
import com.xhj.mybatis.mapping.MappedStatement;
import com.xhj.mybatis.mapping.ParameterMapping;
import com.xhj.mybatis.reflection.MetaObject;
import com.xhj.mybatis.session.Configuration;
import com.xhj.mybatis.type.JdbcType;
import com.xhj.mybatis.type.TypeHandler;
import com.xhj.mybatis.type.TypeHandlerRegistry;

import java.sql.PreparedStatement;
import java.util.List;

/**
 * DefaultParameterHandler
 *
 * @author XJks
 * @description 默认参数处理器
 */
public class DefaultParameterHandler implements ParameterHandler {

    /**
     * 类型处理器注册表
     */
    private final TypeHandlerRegistry typeHandlerRegistry;

    /**
     * MappedStatement 对象，包含了 SQL 映射的信息
     */
    private final MappedStatement mappedStatement;

    /**
     * 用户传入的参数对象
     */
    private final Object parameterObject;

    /**
     * 绑定的 SQL 对象，包含了最终的 SQL 语句和参数映射信息
     */
    private final BoundSql boundSql;

    /**
     * 全局配置对象
     */
    private final Configuration configuration;

    public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        this.mappedStatement = mappedStatement;
        this.configuration = mappedStatement.getConfiguration();
        this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
        this.parameterObject = parameterObject;
        this.boundSql = boundSql;
    }

    @Override
    public Object getParameterObject() {
        return parameterObject;
    }

    @Override
    public void setParameters(PreparedStatement ps) {
        // 获取 SQL 语句中的参数映射列表
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        // 如果参数映射不为空，遍历每一个参数映射
        if (parameterMappings != null) {
            for (int i = 0; i < parameterMappings.size(); i++) {
                // 获取第 i 个参数映射
                ParameterMapping parameterMapping = parameterMappings.get(i);
                // 最终要设置到 SQL 的参数值
                Object value;
                // 参数对应的属性名
                String propertyName = parameterMapping.getProperty();
                // 根据不同情况获取参数值
                if (boundSql.hasAdditionalParameter(propertyName)) {
                    // TODO:如果 boundSql 中有额外的参数，优先从 BoundSql 的附加参数中获取（如 foreach、bind 等动态参数）
                    value = boundSql.getAdditionalParameter(propertyName);
                } else if (parameterObject == null) {
                    // 如果参数对象为 null，则参数值为 null
                    value = null;
                } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    // 如果参数对象有对应的 TypeHandler，直接使用参数对象本身
                    value = parameterObject;
                } else {
                    // 否则通过反射从参数对象中获取对应属性的值
                    MetaObject metaObject = configuration.newMetaObject(parameterObject);
                    value = metaObject.getValue(propertyName);
                }
                // 获取参数的类型处理器
                TypeHandler typeHandler = parameterMapping.getTypeHandler();
                // 获取参数的 JDBC 类型
                JdbcType jdbcType = parameterMapping.getJdbcType();
                // 如果参数值为 null 且未指定 JDBC 类型，则使用全局配置的默认 JDBC 类型
                if (value == null && jdbcType == null) {
                    jdbcType = configuration.getJdbcTypeForNull();
                }
                try {
                    // 调用类型处理器为 PreparedStatement 设置参数
                    typeHandler.setParameter(ps, i + 1, value, jdbcType);
                } catch (Exception e) {
                    // 如果设置参数出错，抛出异常并附带详细信息
                    throw new RuntimeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                }
            }
        }
    }

}
